#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <functional>

#include "socket.hpp"
#include "log.hpp"

#include <sys/epoll.h>

using namespace log_namespace;
using namespace socket_namespace;

class EpollServer
{
    // 只需要让 size 是一个大于零的数即可，这是调用接口要求的
    const static int size = 128;
    const static int numofreadyevent = 128;

public:
    EpollServer(uint16_t port)
        : _port(port), _listensock(std::make_unique<TCPSocket>())
    {
        _listensock->buildListenSocket(port);
        // 这里传入的size没有任何意义
        _epfd = ::epoll_create(size);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll_create error!\n");
            exit(1);
        }
        LOG(INFO, "epoll_create success, epfd: %d\n", _epfd);
    }

    void initServer()
    {
        // 新链接到来，我们认为读事件就绪
        struct epoll_event ev;
        ev.events = EPOLLIN | EPOLLET;
        // 为了在事件就绪的时候，就可以通过fd这个成员确定是哪一个fd就绪了，
        ev.data.fd = _listensock->getsockfd();
        // 必须先把 _listensock 套接字添加到epoll中
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->getsockfd(), &ev);
        if (n < 0)
        {
            LOG(FATAL, "epoll_ctl error!\n");
            exit(1);
        }
        LOG(INFO, "epoll_ctl success, add new listensockfd: %d\n", _listensock->getsockfd());
    }

    std::string eventsToString(uint32_t events)
    {
        std::string eventstr;
        if (events & EPOLLIN)
        {
            eventstr = "EPOLLIN";
        }
        if (events & EPOLLOUT)
        {
            eventstr += "|EPOLLOUT";
        }
        return eventstr;
    }

    void Accepter()
    {
        inetAddr addr;
        int ssockfd = _listensock->Accepter(&addr);
        if (ssockfd < 0)
        {
            LOG(ERROR, "获取连接失败\n");
            return;
        }
        LOG(INFO, "得到一个新的连接：%d, 客户端信息：%s:%d\n", ssockfd, addr.IP().c_str(), addr.Port());
        // 得到了一个新的sockfd, 不能直接进行read, recv，因为不能保证新的连接的读事件就绪了
        // 只有epoll_wait确定了底层有数据(读事件就绪)，才能进行read, recv
        struct epoll_event ev;
        ev.data.fd = ssockfd;
        ev.events = EPOLLIN;
        ::epoll_ctl(_epfd, EPOLL_CTL_ADD, ssockfd, &ev);
        LOG(INFO, "epoll_ctl success, add new sockfd: %d\n", ssockfd);
    }

    void HandleIO(int fd)
    {
        // 由于不同的请求长度不同，所以应用层在接收的时候
        // 也要把这些不同的请求进行分离，所以需要按文件来分割请求
        // 即一个fd对应一个应用层缓冲区
        char buffer[4096];
        int n = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer;
            std::string response = "HTTP/1.0 200 OK\r\n";
            std::string content = "<html><body><h1>hello bit</h1></body></html>";
            response += "Content-Type: text/html\r\n";
            response += "Content-Length" + std::to_string(content.size()) + "\r\n";
            response += "\r\n";
            response += content;
            ::send(fd, response.c_str(), response.size(), 0);
        }
        else if (n == 0)
        {
            // 走到这，说明客户端退出了，需要从红黑树中删除节点和关闭文件描述符
            LOG(INFO, "client quit, close fd: %d\n", fd);
            // 先从红黑树中删除节点
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            // 再关闭文件
            ::close(fd);
        }
        else
        {
            // 走到这，说明recv出错
            LOG(ERROR, "recv error, close fd: %d\n", fd);
            // 先从红黑树中删除节点
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            // 再关闭文件
            ::close(fd);
        }
    }

    void HandlerEvent(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int fd = revs[i].data.fd;
            uint32_t revents = revs[i].events;
            LOG(INFO, "%d这个文件中有事件就绪了，具体事件是：%s\n", fd, eventsToString(revents).c_str());
            if (revents & EPOLLIN)
            {
                if (fd == _listensock->getsockfd())
                {
                    Accepter();
                }
                else
                {
                    HandleIO(fd);
                }
            }
        }
    }

    void loop()
    {
        int timeout = 1000;
        while (true)
        {
            int n = ::epoll_wait(_epfd, revs, numofreadyevent, timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "epoll time out...\n");
                break;
            case -1:
                LOG(ERROR, "epoll error\n");
                break;

            default:
                LOG(INFO, "one of events happened!, n : %d\n", n);
                HandlerEvent(n);
                break;
            }
        }
    }

    ~EpollServer()
    {
        if (_epfd >= 0)
        {
            ::close(_epfd);
        }
        _listensock->close();
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;
    int _epfd;
    struct epoll_event revs[numofreadyevent];
};
